package cn.sofwin.core.utils.http;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import cn.sofwin.core.utils.file.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * HTTP访问工具
 * @author LetCode
 * @since 1.0
 */
public class HttpUtils {
	/** 公用HttpClient */
	private static CloseableHttpClient HttpClient;
	
	/** HTTP请求参数 */
	public static final RequestConfig HttpRequestConfig = RequestConfig.custom().setSocketTimeout(10000).setConnectTimeout(10000).build();
	
	/** 默认字符集 */
	private static final String DEFAULT_CHAR_SET = "UTF-8";
	
	/** 日志记录器 */
	private static Logger logger = LoggerFactory.getLogger(HttpUtils.class);
	
	static{
		try {
			//信任所有的HTTPS证书
			SSLContext sslContext = SSLContext.getInstance("SSL");
			sslContext.init(null, new TrustManager[]{ 
									new X509TrustManager() {
										@Override
										public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {}
					
										@Override
										public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {}
					
										@Override
										public X509Certificate[] getAcceptedIssuers() { return null; }
								}}, 
							new SecureRandom());
			SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext);
			HttpClient = HttpClientBuilder.create().setSSLSocketFactory(sslSocketFactory).setMaxConnTotal(200).setMaxConnPerRoute(200).build();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	

	/**
	 * 下载文件，如果目标文件已存在，则不进行下载操作
	 * @param folder
	 * @param url
	 * @return
	 */
	public static HttpResult download(String folder, String url) {
		return download(folder, url, false);
	}
	
	/**
	 * 下载文件
	 * @param folder		目标文件夹
	 * @param url			下载地址
	 * @param overwrite		如果目标文件已存在，是否覆盖目标文件
	 * @return
	 */
	public static HttpResult download(String folder, String url, boolean overwrite) {
		HttpResult httpResult = null;
		int statusCode;
		CloseableHttpResponse response = null;
		InputStream is = null;
		try {
			File targetFile = new File(folder);
			if(!targetFile.exists())
				targetFile.mkdirs();
			
			String fileName = folder + StringUtils.substringAfterLast(url, "/");
			targetFile = new File(fileName);
			if (overwrite || !targetFile.exists()) {
				HttpGet httpGet = new HttpGet(url);
				httpGet.setConfig(HttpRequestConfig);
				response = HttpClient.execute(httpGet);
	
				statusCode = response.getStatusLine().getStatusCode();
				if (statusCode == 200) {
					HttpEntity entity = response.getEntity();
					if (entity != null) {
						is = entity.getContent();
						FileUtils.writeToFile(is, targetFile, true);
						EntityUtils.consume(entity);
					}
				}
	
				httpResult = new HttpResult(statusCode, "OK");
			} else {
				logger.error("文件已存在：{}", fileName);
			}
	
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (response != null) {
					response.close();
					response = null;
				}
				if (is != null) {
					is.close();
					is = null;
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	
		return httpResult;
	}

	/**
	 * 获取真实的请求地址
	 * @author LetCode
	 * @since 1.0
	 * @param url 目标地址
	 * @param params 参数集合
	 * @return
	 */
	public static String getUrl(String url, Map<String, Object> params) {
		return getUrl(url, params, DEFAULT_CHAR_SET);
	}
	
	/**
	 * 获取真实的请求地址
	 * @author LetCode
	 * @since 1.0
	 * @param url 目标地址
	 * @param params 参数集合
	 * @param charSet 字符编码
	 * @return
	 */
	public static String getUrl(String url, Map<String, Object> params, String charSet) {
		StringBuffer sb = new StringBuffer(url);
		try {
			if((params != null) && (params.size() > 0)){
				if(url.contains("?"))
					sb.append("&");
				else
					sb.append("?");
				
				for (Entry<String, Object> entry : params.entrySet()) 
					sb.append(URLEncoder.encode(entry.getKey(), charSet))
					  .append("=")
					  .append(URLEncoder.encode(String.valueOf(entry.getValue()), charSet))
					  .append("&");
				
				sb.deleteCharAt(sb.length() -1);
			}
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return sb.toString();
	}
	
	/**
	 * GET方式发送HTTP数据
	 * @author LetCode
	 * @since 1.0
	 * @param url 目标地址
	 * @param params 参数集合
	 * @return
	 */
	public static HttpResult sendGet(String url, Map<String, Object> params) {
		return sendGet(url, params, DEFAULT_CHAR_SET);
	}
	
	/**
	 * GET方式发送HTTP数据
	 * @author LetCode
	 * @since 1.0
	 * @param url 目标地址
	 * @param params 参数集合
	 * @param charSet 字符编码
	 * @return
	 */
	public static HttpResult sendGet(String url, Map<String, Object> params, String charSet) {
		HttpResult sendResult = null;
		int statusCode;
		String result;
		CloseableHttpResponse remoteResponse = null;
		try {
			HttpGet httpGet = new HttpGet(getUrl(url, params));
			httpGet.setConfig(HttpRequestConfig);
			remoteResponse = HttpClient.execute(httpGet);
			
			statusCode = remoteResponse.getStatusLine().getStatusCode();
			result = EntityUtils.toString(remoteResponse.getEntity(), charSet);
			sendResult = new HttpResult(statusCode, result);
			if (statusCode != 200) {
				logger.error("\n数据发送失败\n[URL]：{}\n[StatusCode]：{}\n[Result]：\n{}", url, statusCode, result);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if(remoteResponse != null){
					remoteResponse.close();
					remoteResponse = null;
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		return sendResult;
	}
	
	/**
	 * POST方式发送HTTP数据
	 * @author LetCode
	 * @since 1.0
	 * @param url 目标地址
	 * @param params 参数集合
	 * @return
	 */
	public static HttpResult sendPost(String url, Map<String, Object> params) {
		return sendPost(url, params, DEFAULT_CHAR_SET);
	}

	/**
	 * POST方式发送HTTP数据
	 * @author LetCode
	 * @since 1.0
	 * @param url 目标地址
	 * @param params 参数集合
	 * @param charSet 字符编码
	 * @return
	 */
	public static HttpResult sendPost(String url, Map<String, Object> params, String charSet) {
		HttpResult sendResult = null;
		int statusCode;
		String result;
		CloseableHttpResponse remoteResponse = null;
		try {
			HttpPost httpPost = new HttpPost(url);
			httpPost.setConfig(HttpRequestConfig);
			
			List<NameValuePair> nvps = new ArrayList<NameValuePair>();
			for (Entry<String, Object> entry : params.entrySet()) 
				nvps.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())));
			httpPost.setEntity(new UrlEncodedFormEntity(nvps, charSet));
			
			remoteResponse = HttpClient.execute(httpPost);
			
			statusCode = remoteResponse.getStatusLine().getStatusCode();
			result = EntityUtils.toString(remoteResponse.getEntity(), charSet);
			sendResult = new HttpResult(statusCode, result);
			if (statusCode != 200) {
				logger.error("\n数据发送失败\n[URL]：{}\n[StatusCode]：{}\n[Result]：\n{}", url, statusCode, result);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if(remoteResponse != null){
					remoteResponse.close();
					remoteResponse = null;
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		return sendResult;
	}
	
	/**
	 * 直接POST字符串数据
	 * @author LetCode
	 * @since 1.0
	 * @param url 目标地址
	 * @param data 参数集合
	 * @return
	 */
	public static HttpResult sendPost(String url, String data) {
		return sendPost(url, data, DEFAULT_CHAR_SET);
	}
	
	/**
	 * 直接POST字符串数据
	 * @author LetCode
	 * @since 1.0
	 * @param url 目标地址
	 * @param data 参数集合
	 * @param charSet 字符编码
	 * @return
	 */
	public static HttpResult sendPost(String url, String data, String charSet) {
		HttpResult sendResult = null;
		int statusCode;
		String result;
		CloseableHttpResponse remoteResponse = null;
		try {
			HttpPost httpPost = new HttpPost(url);
			httpPost.setConfig(HttpRequestConfig);
			httpPost.setEntity(new StringEntity(data, charSet));
			
			remoteResponse = HttpClient.execute(httpPost);
			
			statusCode = remoteResponse.getStatusLine().getStatusCode();
			result = EntityUtils.toString(remoteResponse.getEntity(), charSet);
			sendResult = new HttpResult(statusCode, result);
			if (statusCode != 200) {
				logger.error("\n数据发送失败\n[URL]：{}\n[StatusCode]：{}\n[Result]：\n{}", url, statusCode, result);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if(remoteResponse != null){
					remoteResponse.close();
					remoteResponse = null;
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		return sendResult;
	}
}
